// let Cesium = require('Cesium')
import * as turf from '@turf/turf'

const x_PI = 3.14159265358979324 * 3000.0 / 180.0;
const PI = 3.1415926535897932384626;
const a = 6378245.0;
const ee = 0.00669342162296594323;

/**
 * 基础方法工具类
 */
export default class BasicTool {

    /**
     * 构造函数
     * @param {Object} viewer 
     */
    constructor(viewer) {
        this.viewer = viewer
    }

    /**
     * 通过name找到图片图层
     * @param {String} name 图层的name属性的值
     * @param {*} dataSource 图层的source
     * @returns 
     */
    findImageLayer(name, dataSource) {
        var layer = dataSource ? dataSource : this.viewer.imageryLayers;
        if (layer) {
            var dlayer = layer._layers.find(layer => layer.name == name);
            if (dlayer) {
                return dlayer
            }
            return null;
        }
    }

    /**
     * 移除图片图层
     * @param {*} layer 图层对象
     * @param {*} dataSource 所属图层的父级图层
     * @returns 
     */
    removeImageLayer(layer, dataSource) {
        var imageLayers = dataSource ? dataSource : this.viewer.imageryLayers;
        console.log("imageLayers", imageLayers)
        if (imageLayers.remove) {
            imageLayers.remove(layer)
            return;
        }
    }
    /**
     * 关于viewer的相关初始化设置
     */
    viewerInitSetting() {
        this.viewer.scene.backgroundColor = Cesium.Color.TRANSPARENT;
        this.viewer.scene.globe.baseColor = Cesium.Color.TRANSPARENT;
        //设置操作习惯(禁止中间滑轮调整视角)
        this.viewer.scene.screenSpaceCameraController.zoomEventTypes = [Cesium.CameraEventType.WHEEL, Cesium.CameraEventType.PINCH];
        this.viewer.scene.screenSpaceCameraController.tiltEventTypes = [Cesium.CameraEventType.PINCH, Cesium.CameraEventType.RIGHT_DRAG];
        //开启抗锯齿
        this.openFaxx();
    }

    /**
     * 移除事件
     */
    removeInputAction() {
        // 双击鼠标左键清除默认事件
        this.viewer.cesiumWidget.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);
        // 单击鼠标左键清除默认事件
        this.viewer.cesiumWidget.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
    }

    /**
     * 开启深度地形监测，当缩放地图时会使地图entity被3dTile模型挡住
     */
    openDepthTestAgainstTerrain() {
        //开启深度监测
        this.viewer.scene.globe.depthTestAgainstTerrain = true;
    }

    /**
     * 关闭深度地形监测
     */
    closeDepthTestAgainstTerrain() {
        //关闭深度监测
        this.viewer.scene.globe.depthTestAgainstTerrain = false;
    }
    /**
     * 开启抗锯齿
     */
    openFaxx() {
        //是否开启抗锯齿
        this.viewer.scene.fxaa = true;
        this.viewer.scene.postProcessStages.fxaa.enabled = true;
        if (Cesium.FeatureDetection.supportsImageRenderingPixelated()) {//判断是否支持图像渲染像素化处理
            // 直接拿到设备的像素比例因子 - 如我设置的1.25
            var vtxf_dpr = window.devicePixelRatio;
            // 这个while我们在后面会做一个说明，但不是解决问题的说明
            while (vtxf_dpr >= 2.0) {
                vtxf_dpr /= 2.0;
            }
            // 设置渲染分辨率的比例因子
            this.viewer.resolutionScale = vtxf_dpr;
        }
    }
    /**
     * 关闭抗锯齿
     */
    closeFaxx() {
        //是否开启抗锯齿
        this.viewer.scene.fxaa = false;
        this.viewer.scene.postProcessStages.fxaa.enabled = false;
        this.viewer.resolutionScale = 1.0
    }
    /**
     * 跳转初始化位置
     * @param {*} center 
     */
    goToInit(center) {
        this.setView(
            center[0],
            center[1],
            center[2]
        );
    }
    /**
     * 设置视图
     *
     * @param {*} x
     * @param {*} y
     * @param {*} z
     */
    setView(x, y, z, heading, pitch, roll) {
        //设置当前视图
        this.viewer.camera.setView({
            // Cesium的坐标是以地心为原点，一向指向南美洲，一向指向亚洲，一向指向北极州
            // fromDegrees()方法，将经纬度和高程转换为世界坐标
            destination: Cesium.Cartesian3.fromDegrees(x, y, z),
            orientation: {
                // 指向
                heading: Cesium.Math.toRadians(heading ? heading : 0.0), // east, default value is 0.0 (north)
                // 视角
                pitch: Cesium.Math.toRadians(pitch ? pitch : -90),  // default value (looking down)
                roll: roll ? roll : 0.0, // default value
            },
        });
    }
    /**
     * 鼠标点击时的entity
     *
     * @param {*} position 鼠标事件的e.position对象
     */
    pickEntity(position) {
        var pick = this.viewer.scene.pick(position);//选取当前的entity
        var pickedEntity = Cesium.defined(pick) ? pick.id : undefined; // pick.id即为entity
        if (pickedEntity) {
            return pickedEntity;
        }
        return null;
    }
    /**
     * 鼠标移动时坐标获取cartesian
     *
     * @param {*} position
     */
    getCartesianByPosition(position) {
        var cartesian = this.viewer.scene.camera.pickEllipsoid(
            position,
            this.viewer.scene.globe.ellipsoid
        );
        return cartesian;
    }
    /**
     * 设置俯视视角
     */
    setLookDownPitch() {
        this.viewer && this.viewer.camera.setView({
            orientation: {
                // 视角
                pitch: Cesium.Math.toRadians(-90),
            }
        })
    }

    /**
     * cesium是否开启SCENE3D模式
     * @param {*} viewer 
     * @returns 
     */
    isEqu3DMode(viewer) {
        var viewer = viewer ? viewer : this.viewer;
        if (viewer.scene.mode == Cesium.SceneMode.SCENE3D) {
            return true;
        }
        return false;
    }
    /**
     * 3D视图下锁定/解锁相机
     * @param {*} enable 
     * @param {*} viewer 
     */
    lockCameraRotate(enable, viewer) {
        var viewer = viewer ? viewer : this.viewer;
        viewer.scene.screenSpaceCameraController.enableRotate = !enable;//锁定相机
    }
    /**
     * 2D视图下锁定/解锁相机
     * @param {*} enable 
     * @param {*} viewer 
     */
    lock2DViewer(enable, viewer) {
        var viewer = viewer ? viewer : this.viewer;
        // 如果为true，则允许用户平移地图。如果为假，相机将保持锁定在当前位置。此标志仅适用于2D和Columbus视图模式。
        viewer.scene.screenSpaceCameraController.enableTranslate = !enable;//锁定相机
    }
    /**
     * 计算两点中心位置
     */
    computeCenterPotition(p1, p2) {
        var c1 = this.viewer.scene.globe.ellipsoid.cartesianToCartographic(p1);
        var c2 = this.viewer.scene.globe.ellipsoid.cartesianToCartographic(p2);
        var cm = new Cesium.EllipsoidGeodesic(c1, c2).interpolateUsingFraction(0.5);
        var cp = this.viewer.scene.globe.ellipsoid.cartographicToCartesian(cm);
        return cp;
    }
    /**
     * 获取polygon多边形下的中心点坐标
     *
     * @param {*} positions positions对象
     * @returns {Cartesian3} 返回一个Cartesian3对象
     */
    getCenterByPosition(positions) {
        // 获取多边形的positions列表 并计算它的中心点
        var polyCenter = Cesium.BoundingSphere.fromPoints(positions).center;
        polyCenter = Cesium.Ellipsoid.WGS84.scaleToGeodeticSurface(polyCenter);
        return polyCenter;
    }
    /**
     * 世界坐标点转屏幕坐标点
     *
     * @param {*} position 世界坐标点
     * @return {*}
     */
    cartesianToWinPos(position) {
        var position = position._value ? position._value : position;
        var winPos = this.viewer.scene.cartesianToCanvasCoordinates(position);
        return winPos;
    }
    /**
     * 从x,y,z获取cartesian3，转换为经纬度
     *
     * @param {*} x
     * @param {*} y
     * @param {*} z
     * @return {*}
     * {
    x:lon,
    y:lat,
    z:alt
  }
     */
    toCartographicFromXyz(x, y, z) {
        var ellipsoid = this.viewer.scene.globe.ellipsoid;
        var cartesian3 = new Cesium.cartesian3(x, y, z);
        var cartographic = ellipsoid.cartesianToCartographic(cartesian3);
        var lat = Number(Cesium.Math.toDegrees(cartographic.latitude)).toFixed(6);
        var lon = Number(Cesium.Math.toDegrees(cartographic.longitude)).toFixed(6);
        var alt = Number(cartographic.height).toFixed(2);
        return {
            x: lon,
            y: lat,
            z: alt
        }
    }

    /**
     * 世界坐标系转换为经纬度对象
     *
     * @param {*} cartesian 世界坐标系对象
     * @return {*}
     */
    toCartographic(cartesian) {
        var cartographic = Cesium.Cartographic.fromCartesian(cartesian),
            result = {};
        return result.y = Number(Cesium.Math.toDegrees(cartographic.latitude)).toFixed(6),
            result.x = Number(Cesium.Math.toDegrees(cartographic.longitude)).toFixed(6),
            result.z = Number(cartographic.height).toFixed(2),
            result
    }

    /**
     * 创建一个Cartesian3对象
     *
     * @param {*} x 经度
     * @param {*} y 纬度
     * @param {*} z 海拔
     * @return {*}
     */
    createPoint(x, y, z) {
        var pt = new Cesium.Cartesian3.fromDegrees(Number(x ? x : 0), Number(y ? y : 0), Number(z ? z : 0));
        return pt;
    }
    /**
     * 笛卡尔坐标转换为地理坐标,以对象的形式返回
     *
     * @param {*} cartesian 笛卡尔坐标对象
     * @param {*} isToWgs84 是否转换为WGS84
     * @return {*}
     */
    cartesian2LonLat(cartesian) {
        //将笛卡尔坐标转换为地理坐标
        var cartographic = this.viewer.scene.globe.ellipsoid.cartesianToCartographic(
            cartesian
        );
        //将弧度转为度的十进制度表示
        var pos = {
            lon: Cesium.Math.toDegrees(cartographic.longitude),
            lat: Cesium.Math.toDegrees(cartographic.latitude),
            alt: Math.ceil(cartographic.height),
        };
        return pos;
    }
    /**
        * 屏幕坐标转世界坐标
        * @param {*} cartesian2 屏幕坐标对象
        * @return {*}
        */
    win2Cartesian3(cartesian2) {
        if (cartesian2) {
            var cartesian3 = this.viewer.scene.globe.pick(this.viewer.camera.getPickRay(cartesian2), this.viewer.scene);
            return cartesian3;
        }
    }
    /**
     * 笛卡尔坐标转换为地理坐标,以数组的形式返回
     *
     * @param {*} cartesian 笛卡尔坐标对象
     * @return {*}
     */
    cartesian2ArrayLonLat(cartesian) {
        if (!cartesian) return;
        //将笛卡尔坐标转换为地理坐标
        var cartographic = this.viewer.scene.globe.ellipsoid.cartesianToCartographic(
            cartesian
        );
        //将弧度转为度的十进制度表示
        var pos = [Cesium.Math.toDegrees(cartographic.longitude), Cesium.Math.toDegrees(cartographic.latitude), Math.ceil(cartographic.height)]
        return pos;
    }
    /**
     * 笛卡尔坐标转换为地理坐标,以字符串的形式返回
     *
     * @param {*} cartesian 笛卡尔坐标对象
     * @return {*}
     */
    cartesian2StringLonLat(cartesian) {
        if (!cartesian) return;
        //将笛卡尔坐标转换为地理坐标
        var cartographic = this.viewer.scene.globe.ellipsoid.cartesianToCartographic(
            cartesian
        );
        //将弧度转为度的十进制度表示
        var pos = `${Cesium.Math.toDegrees(cartographic.longitude)},${Cesium.Math.toDegrees(cartographic.latitude)},${Math.ceil(cartographic.height)}`
        return pos;
    }
    /**
     * 经纬度转世界坐标
     * @param {Array} lnglat
     * @returns
     */
    lnglatToCartesian(lnglat) {
        if (!lnglat) return null;
        return Cesium.Cartesian3.fromDegrees(lnglat[0], lnglat[1], lnglat[2] || 0);
    }
    /**
     * 经纬度转世界坐标(返回数组)
     * @param {Array} lnglat
     * @returns
     */
    lnglatArrToCartesianArr(lnglatArr) {
        if (!lnglatArr) return [];
        var arr = [];
        for (var i = 0; i < lnglatArr.length; i++) {
            arr.push(this.lnglatToCartesian(lnglatArr[i]));
        }
        return arr;
    }
    transformWD(lng, lat) {
        var ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * Math.sqrt(Math.abs(lng));
        ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(lat * PI) + 40.0 * Math.sin(lat / 3.0 * PI)) * 2.0 / 3.0;
        ret += (160.0 * Math.sin(lat / 12.0 * PI) + 320 * Math.sin(lat * PI / 30.0)) * 2.0 / 3.0;
        return ret;
    }
    transformJD(lng, lat) {
        var ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng));
        ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(lng * PI) + 40.0 * Math.sin(lng / 3.0 * PI)) * 2.0 / 3.0;
        ret += (150.0 * Math.sin(lng / 12.0 * PI) + 300.0 * Math.sin(lng / 30.0 * PI)) * 2.0 / 3.0;
        return ret;
    }
    wgs2gcj(arrdata) {
        var lng = Number(arrdata[0]);
        var lat = Number(arrdata[1]);
        var dlat = this.transformWD(lng - 105.0, lat - 35.0);
        var dlng = this.transformJD(lng - 105.0, lat - 35.0);
        var radlat = lat / 180.0 * PI;
        var magic = Math.sin(radlat);
        magic = 1 - ee * magic * magic;
        var sqrtmagic = Math.sqrt(magic);
        dlat = dlat * 180.0 / (a * (1 - ee) / (magic * sqrtmagic) * PI);
        dlng = dlng * 180.0 / (a / sqrtmagic * Math.cos(radlat) * PI);
        var mglat = lat + dlat;
        var mglng = lng + dlng;

        mglng = Number(mglng.toFixed(6));
        mglat = Number(mglat.toFixed(6));
        return [mglng, mglat];
    }
    gcj2wgs(arrdata) {
        var lng = Number(arrdata[0]);
        var lat = Number(arrdata[1]);
        var dlat = this.transformWD(lng - 105.0, lat - 35.0);
        var dlng = this.transformJD(lng - 105.0, lat - 35.0);
        var radlat = lat / 180.0 * PI;
        var magic = Math.sin(radlat);
        magic = 1 - ee * magic * magic;
        var sqrtmagic = Math.sqrt(magic);
        dlat = dlat * 180.0 / (a * (1 - ee) / (magic * sqrtmagic) * PI);
        dlng = dlng * 180.0 / (a / sqrtmagic * Math.cos(radlat) * PI);

        var mglat = lat + dlat;
        var mglng = lng + dlng;

        var jd = lng * 2 - mglng;
        var wd = lat * 2 - mglat;

        jd = Number(jd.toFixed(6));
        wd = Number(wd.toFixed(6));
        return [jd, wd];

    }
    /**
     * 计算面积
     *
     * @param {*} positions 点位对象
     * @return {*}
     */
    computeArea(points) {
        var area = 0;
        for (let i = 0; i < points.length; i++) {
            let j = (i + 1) % points.length;
            area += points[i].x * points[j].y;
            area -= points[i].y * points[j].x;
        }
        area /= 2;
        return Math.abs(area);
    }

    computeAreaTurf(positions){
        var arr = [];
        for (var i = 0; i < positions.length; i++) {
            var p = this.cartesian2LonLat(positions[i]);
            arr.push([p.lon, p.lat]);
        }
        arr.push(arr[0]); //终点和起点重合

        var polygon = turf.polygon([arr]);
        var area = turf.area(polygon) / 1000 / 1000;
        return area;
    }

    computeAreaTurfMu(positions){
        let area = this.computeAreaTurf(positions) / 666.67; // 转成亩
        return area.toFixed(2);
    }

    computeAreaMu(positions){
        let area = this.computeArea(positions) / 666.67; // 转成亩
        return area.toFixed(2);
    }
    /**
     * 3D下计算周长
     *
     * @param {*} pntList
     * @return {*}
     */
    computeLineDis3d(pntList) {
        var total = 0;
        for (var i = 1; i < pntList.length; i++) {
            var p1 = pntList[i - 1];
            var p2 = pntList[i];
            var dis = this.computeDis3d(p1, p2);
            total += dis;
        }
        return total;
    }

    /**
     * 3D下计算圆半径
     *
     * @param {Array} positions 点位数组
     * @return {*}
     */
    computeCircleRadius3D(positions) {
        var c1 = positions[0];
        var c2 = positions[1];
        var x = Math.pow(c1.x - c2.x, 2);
        var y = Math.pow(c1.y - c2.y, 2);
        var z = Math.pow(c1.z - c2.z, 2);
        var dis = Math.sqrt(x + y + z);
        return dis;
    }
    /**
     * 计算圆
     *
     * @param {*} positions
     * @return {*}
     */
    computeCirclePolygon(positions) {
        try {
            if (!positions || positions.length < 2) {
                return null;
            }
            var cp = positions[0];
            var r = this.computeCircleRadius3D(positions);
            var pnts = this.computeCirclePolygon2(cp, r);
            return pnts;
        } catch (err) {
            return null;
        }
    }
    /**
     * 添加entity
     *
     * @param {*} entity 实体对象
     * @param {*} layer 指定到某个图层中(dataSource)
     */
    addEntity(entity, layer) {
        //默认添加到viewer.entities中。设备对象的话因为是需要聚合的，所以添加到this.layers.deviceLayer
        entity.billboard.disableDepthTestDistance = Number.POSITIVE_INFINITY; //去掉地形遮挡
        if (layer) {
            layer.entities.add(entity);
            return;
        }
        this.viewer.entities.add(entity);
    }
    /**
     * 隐藏entity
     *
     * @param {*} entity entity对象
     * @return {*}
     */
    hideEntity(entity) {
        entity.show = false;
        return this;
    }
    /**
     * 显示entity
     *
     * @param {*} entity 需要显示的entity
     * @return {*}
     */
    showEntity(entity) {
        entity.show = true;
        return this;
    }

    /**
     * 删除实体
     *
     * @param {*} entity 实体对象
     */
    deleteEntity(entity, dataSource) {
        var layer = dataSource ? dataSource.entities : this.viewer.entities;
        layer.remove(entity);
    }

    /**
     * 删除实体
     *
     * @param {*} entitys 实体对象
     */
    deleteEntitys(entitys, dataSource) {
        var layer = dataSource ? dataSource.entities : this.viewer.entities;
        for (var i = 0, len = entitys.length; i < len; i++) {
            layer.remove(entitys[i]);
        }
    }

    /**
     * ID查询实体
     *
     * @param {*} id
     * @return {*}
     */
    getEntityById(id) {
        //获取一个实体
        var entity = this.viewer.entities.getById(id);
        return entity;
    }
    /**
     * 创建label.具体选项参考[LabelGraphics]{@link http://cesium.xin/cesium/cn/Documentation1.62/LabelGraphics.html}
     *
     * @param {*} opt 选项
     * @return {*}
     */
    createLabelGraphic(opt) {
        return new Cesium.LabelGraphics(opt);
    }

    /**
     * 定位到图标（带飞行动画）
     * @param {*} entity 目标实体
     * @returns 
     */
    goToLayer(entity) {
        this.viewerFlyTo(entity, 1);
        return this;
    }

     /**
     * 直接定位到图标
     * @param {*} entity 
     */
    panTo(entity) {
        window.cesMap.mapTool.zoomTo(entity);
    }

    /**
     * 飞行效果定位
     *
     * @param {*} x
     * @param {*} y
     * @param {*} z
     * @param {*} duration
     */
    flyTo(x, y, z, duration) {
        this.viewer.camera.flyTo({
            destination: Cesium.Cartesian3.fromDegrees(x, y, z),
            orientation: {
                //设置相机的Heading，Pitch，Roll
                heading: Cesium.Math.toRadians(90.0),
                pitch: Cesium.Math.toRadians(-90),
                roll: 0.0,
            },
            duration: duration >= 0 ? duration : 5.0,
            pitchAdjustHeight: -90, // 如果摄像机飞越高于该值，则调整俯仰俯仰的俯仰角度，并将地球保持在视口中。
            maximumHeight: 5000, // 相机最大飞行高度
            flyOverLongitude: 100, // 如果到达目的地有2种方式，设置具体值后会强制选择方向飞过这个经度
        });
    }

    /**
     * 飞行效果定位
     *
     * @param {*} data Cartesian3 | Rectangle
     * @param {*} duration
     */
    flyTo2(cartesian3, duration) {
        this.viewer.camera.flyTo({
            destination: cartesian3,
            orientation: {
                //设置相机的Heading，Pitch，Roll
                heading: Cesium.Math.toRadians(90.0),
                pitch: Cesium.Math.toRadians(-90),
                roll: 0.0,
            },
            duration: duration >= 0 ? duration : 5.0,
            offset: new Cesium.HeadingPitchRange(0.0, Cesium.Math.toRadians(-20.0)),
            pitchAdjustHeight: -90, // 如果摄像机飞越高于该值，则调整俯仰俯仰的俯仰角度，并将地球保持在视口中。
            maximumHeight: 5000, // 相机最大飞行高度
            flyOverLongitude: 100, // 如果到达目的地有2种方式，设置具体值后会强制选择方向飞过这个经度
        });
    }

    /**
     * viewer 的 flyTo方法
     * @param {*} target 目标。Entity | Array.< Entity > | EntityCollection | DataSource 更过具体对象可以看[viewer]{@link http://cesium.xin/cesium/cn/Documentation1.72/Viewer.html?classFilter=viewer}
     * @param {*} duration  传入大于0整数。单位秒，如5代表5秒而不是传5000
     */
    viewerFlyTo(target, duration) {
        this.viewer.flyTo(target, {
            duration: duration >= 0 ? duration : 5,
            offset: new Cesium.HeadingPitchRange(0.0, Cesium.Math.toRadians(-20.0))
        })
    }

    /**
     * 直接定位到entity
     *
     * @param {*} entity entity
     */
    zoomTo(entity, heading, pitch, range) {
        this.viewer.trackedEntity = undefined;
        this.viewer.zoomTo(entity, new Cesium.HeadingPitchRange(heading ? heading : 0, pitch ? Cesium.Math.toRadians(pitch) : Cesium.Math.toRadians(-90), range ? range : 0))
    }

    /**
     * 定位到要素
     * @param {*} data
     */
    zoomTo2(data) {
        this.viewer.zoomTo(data, new Cesium.HeadingPitchRange(0.5, -0.2, data.boundingSphere.radius * 1.0));
    }

    /**
     * 创建dataSource
     * @param {Sting} name 
     * @returns 
     */
    createDataSource(name) {
        var layer = new Cesium.CustomDataSource(name)
        return this.viewer.dataSources.add(
            layer,
            {
                clampToGround: true //开启贴地
            }
        )
    }

    /**
     * wgs84转屏幕坐标
     * @param {*} position 
     * @returns 
     */
    wgs84ToWind(position) {
        const windowPosition = new Cesium.Cartesian2();
        return Cesium.SceneTransforms.wgs84ToWindowCoordinates(this.viewer.scene, position, windowPosition)
    }

    wgs84ToWind3(position) {
        const windowPosition = new Cesium.Cartesian3();
        return Cesium.SceneTransforms.wgs84ToWindowCoordinates(this.viewer.scene, position, windowPosition)
    }
}
